package org.chispa.api.hashers;

import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;

import javax.annotation.Nullable;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.chispa.api.util.NoSuitableVisitorException;

public class StringHash implements IndexingHash<String, String> {
	
	private char[] initialHash;
	protected Mac mac;
	public static final String DEF_HASH="HmacSHA1";
	private BigInteger size;
	protected SecretKeySpec secret;
	
	public StringHash(int size,@Nullable String hmac,@Nullable String algorithm) throws NoSuchAlgorithmException
	{
		if(algorithm!=null)
		{
			mac=Mac.getInstance(algorithm);
			secret = new SecretKeySpec(hmac.getBytes(),algorithm);
		}
		this.size=BigInteger.valueOf(size);
		initialHash=new char[size];
		for(int i=0;i<size;i++)
			initialHash[i]='0';
	}
	
	public StringHash(int size,@Nullable String hmac) throws NoSuchAlgorithmException
	{
		this(size,hmac,DEF_HASH);
	}
	
	public StringHash(int size) throws NoSuchAlgorithmException
	{
		this(size,null,null);
	}
	
	@Override
	public String computeHash(String fromValue) throws NoSuitableVisitorException {
		ArrayList<Integer> changed=new ArrayList<Integer>();
		for(int i=0;i<fromValue.length()-1;i++)
		{
			char first=fromValue.charAt(i);
			char second=fromValue.charAt(i+1);
			int num;
			int rem;
			if(secret!=null)
			{
				try {
					mac.init(secret);
				} catch (InvalidKeyException e) {
					throw new NoSuitableVisitorException(e);
				}
				String indexStr=new String(new char[]{first,second});
				byte[]enc=mac.doFinal(indexStr.getBytes());
				BigInteger encNum=new BigInteger(enc);
				rem=encNum.mod(size).intValue();
			}
			else
			{
				num=((int) first)*100+(int)second;
				rem=num%size.intValue();
			}
			initialHash[rem]='1';
			changed.add(rem);
		}
		String toRet=new String(initialHash);
		for(Integer toChange:changed)
		{
			initialHash[toChange]='0';
		}
		return toRet;
	}

}
